
#include <iostream>
#include <fstream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string.hpp>

#include "Parser.hpp"
#include "Unigrama.hpp"
#include "Bigrama.hpp"
#include "Trigrama.hpp"
#include "manejoDeNgramas.hpp"

using namespace std;
using namespace boost;

#define RUTA_ARCHIVO "/home/nico/train2.txt"
//#define RUTA_ARCHIVO "train.txt"
#define ERROR_RUTA_ARCHIVO -1;

const size_t MAX_BUFFER = 10000000; // AJUSTAR SI SE QUEDA SIN MEMORIA saque 2 ceros


bool tokenValido (string token, set <string> &caracteresInvalidos){

	set<string>:: iterator iterador = caracteresInvalidos.begin();
	bool esValido = true;

	while ((iterador != caracteresInvalidos.end()) && (esValido)){
		if ((token.compare(*iterador) == 0)){
				esValido = false;
			}
		iterador++;
	}
	return esValido;
}

void cargarCaracteresInvalidos (set <string> &caracteres){

	caracteres.insert("#");
	caracteres.insert(".");
	caracteres.insert(",");
	caracteres.insert(":");
	caracteres.insert("¿");
	caracteres.insert("?");
	caracteres.insert("!");
	caracteres.insert("¡");
	caracteres.insert("-");
	caracteres.insert("/");
	caracteres.insert("(");
	caracteres.insert(")");
	caracteres.insert(";");
	caracteres.insert("-");
	caracteres.insert("·");
	caracteres.insert("{");
	caracteres.insert("}");
	caracteres.insert("|");
	caracteres.insert("~");
	caracteres.insert("©");
	caracteres.insert("\"");
	caracteres.insert("�");
	caracteres.insert("*");
	caracteres.insert("+");
	caracteres.insert("=");
	caracteres.insert("<");
	caracteres.insert(">");
	caracteres.insert("[");
	caracteres.insert("]");
	caracteres.insert("_");
	caracteres.insert("..");
	caracteres.insert("...");
	caracteres.insert("....");
	caracteres.insert(".....");
}

void verificarNumero(string* token){

	string s = *token;
	erase_all(s,",");
	erase_all(s,".");
	erase_all(s,"-");

	try{
		lexical_cast<double>(s);
	}catch (const bad_lexical_cast &){
			return;
		}
	*token = "<Num>";
}

void separar (string token, vector<string>* v){
	split(*v,token,is_any_of("-"));
}

int main() {

	/* TODO:
	 * 		-mergear todos los archivos y armar archivo modelo (ver donde y como se calculan las probabilidades)
	 * 		-implementar funcion de hash para cargar modelo en memoria
	 * 		-procesar archivo de prueba
	 */


	ifstream archivoEntrenamiento (RUTA_ARCHIVO);
	map <string,Unigrama*> mapaUnigramas;
	map <string,Unigrama*>:: iterator iteradorUnigramas;
	map <string,Bigrama*> mapaBigramas;
	map <string,Bigrama*>:: iterator iteradorBigramas;
	map <string,Trigrama*> mapaTrigramas;
	map <string,Trigrama*>:: iterator iteradorTrigramas;

	set <string> caracteresInvalidos;
	vector <string> terminos;
	vector <string> vaux;
	Parser parser;
	string cadena;
	string token;
	int contador = 0;
	int version = 1;
	unsigned int totalLeido = 0;
	size_t bytesUsados = 0;
	bool salir = false;
	if (!archivoEntrenamiento){
		cout << "Error en la ruta de archivo";
		return ERROR_RUTA_ARCHIVO;
	}

	cargarCaracteresInvalidos (caracteresInvalidos);
	bytesUsados = 48*3; //48 bytes iniciales por cada mapa
	vaux.empty();

	while ( getline(archivoEntrenamiento,cadena) && !salir){
		contador++;
		if (contador == 500){
			cout << "Se procesaron 500 lineas" << endl;
			totalLeido = totalLeido + contador;
			contador = 0;
		}
		transform(cadena.begin(), cadena.end(), cadena.begin(), ::tolower);
		parser.tokenizar(&cadena);
		while (!parser.finTokens()){
			token = parser.getToken();
			if (tokenValido (token,caracteresInvalidos)){
				//comparar si el split mejora o no la precision
//				verificarNumero(&token);
//				terminos.push_back(token);
				separar(token,&vaux);
				while (!vaux.empty()){
					token = vaux.front();
					verificarNumero(&token);
					terminos.push_back(token);
					vaux.erase(vaux.begin());
				}

			}
		}

		guardarNgramas(mapaUnigramas,mapaBigramas,mapaTrigramas,terminos,&bytesUsados);
		terminos.clear(); // vacio el vector de terminos
		if (bytesUsados > MAX_BUFFER){
			cout << "se leyeron: " << totalLeido << " lineas" << endl;
			//cout << "se usaron: " << bytesUsados << " bytes" << endl;
			serializarNgramas(mapaUnigramas,mapaBigramas,mapaTrigramas,version);
			cout << "vaciando mapas... " << endl;
			borrarUnigramas (mapaUnigramas);
			borrarBigramas (mapaBigramas);
			borrarTrigramas (mapaTrigramas);
			bytesUsados = 48*3; //reinicio con el tamanio de los 3 mapas vacios
			//salir = true; // para que corte el ciclo de lectura la primera vez que se llena un mapa
			version++;
		}
	}

	if ( (!mapaUnigramas.empty()) || (!mapaBigramas.empty()) || (!mapaTrigramas.empty())){
		cout << "se leyeron: " << totalLeido+contador << " lineas" << endl;
		cout << "se usaron: " << bytesUsados << " bytes" << endl;
		serializarNgramas(mapaUnigramas,mapaBigramas,mapaTrigramas,version);
		cout << "vaciando mapas... " << endl;
		borrarUnigramas (mapaUnigramas);
		borrarBigramas (mapaBigramas);
		borrarTrigramas (mapaTrigramas);
	}

//	mergearUnigramas(mapaUnigramas,version);
//	mergearBigramas(mapaBigramas,version);
//	mergearTrigramas(mapaTrigramas,version);
//	serializarNgramas(mapaUnigramas,mapaBigramas,mapaTrigramas,15);

//	mostrarUnigramas(mapaUnigramas);
//  mostrarBigramas(mapaBigramas);
//  mostrarTrigramas(mapaTrigramas);
//	borrarUnigramas(mapaUnigramas);
//	borrarBigramas(mapaBigramas);
//	borrarTrigramas(mapaTrigramas);

}//FIN PROG



//
//typedef struct trig{
//    string clave;
//    string opcion;
//    int ocurrencias;
//};
//
//void hacerTrigrama(string c1, string c2, string c3){
//    trig tri;
//    tri.clave = c1 + c2;
//    tri.opcion = c3;
//    tri.ocurrencias = 1;
//}
//
//int main() {
//    string cadena1;
//    string cadena2;
//    string cadena3;
//    ifstream train("train_v2.txt");
//
//    train >> cadena1;
//    train >> cadena2;
//    while (!train.eof()) {
//        train >> cadena3;
//        cout << cadena3 << " ";
//        hacerTrigrama(cadena1, cadena2, cadena3);
//        cadena1 = cadena2;
//        cadena2 = cadena3;
//    }
//
//    train.close();
//
//
//    ifstream test("test_v2.txt");
//
//    train >> cadena1;
//    train >> cadena2;
//    while (!test.eof()){
//        train >> cadena3;
//        /*agregar puntaje y locacion*/
//        cadena1 = cadena2;
//        cadena2 = cadena3;
//        if (cadena3[cadena3.size()-1] == '.') {
//            cin >> cadena3;
//            /*buscar maximo y agregar palabra*/
//            /*borrar puntajes y locaciones*/
//
//        }
//    }
//    test.close();
//
//    return 0;
//}
